package com.homestead.config;

import com.homestead.domain.model.SignInIdentity;
import com.homestead.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.util.LinkedHashMap;

/**
 * 授权服务
 * @author 1
 */
@Configuration
@EnableAuthorizationServer
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    private final RedisTokenStore redisTokenStore;
    private final AuthenticationManager authenticationManager;
    private final PasswordEncoder passwordEncoder;
    private final UserService userService;

    @Value(value = "${client.oauth2.client-id:appId}")
    private String clientId;

    @Value(value = "${client.oauth2.secret:123456}")
    private String secret;

    @Value(value = "${client.oauth2.grant_types:}")
    private String[] grantTypes;

    @Value(value = "${client.oauth2.token-validity-time:}")
    private int tokenValidityTime;

    @Value(value = "${client.oauth2.refresh-token-validity-time:}")
    private int refreshTokenValidityTime;

    @Value(value = "${client.oauth2.scopes:}")
    private String[] scopes;

    /**
     * 配置令牌端点安全约束
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 允许访问 token 的公钥，默认 /oauth/token_key 是受保护的
        security.tokenKeyAccess("permitAll()")
                // 允许检查 token 的状态，默认 /oauth/check_token 是受保护的
                .checkTokenAccess("permitAll()");
    }

    /**
     * 客户端配置 - 授权模型
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // 客户端标识 ID
                .withClient(clientId)
                // 客户端安全码
                .secret(passwordEncoder.encode(secret))
                // 授权类型
                .authorizedGrantTypes(grantTypes)
                // token 有效期
                .accessTokenValiditySeconds(tokenValidityTime)
                // 刷新 token 的有效期
                .refreshTokenValiditySeconds(refreshTokenValidityTime)
                // 客户端访问范围
                .scopes(scopes);
    }

    /**
     * 配置授权以及令牌的访问端点和令牌服务
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 认证器
        endpoints.authenticationManager(authenticationManager)
                // 具体登录的方法
                .userDetailsService(userService)
                // token 存储的方式：Redis
                .tokenStore(redisTokenStore)
                // 令牌增强对象，增强返回的结果
                .tokenEnhancer((accessToken, authentication) -> {
                    // 获取登录用户的信息，然后设置
                    SignInIdentity signInIdentity = (SignInIdentity) authentication.getPrincipal();
                    LinkedHashMap<String, Object> map = new LinkedHashMap<>();
                    map.put("mobile", signInIdentity.getMobile());
                    map.put("username", signInIdentity.getUsername());
                    DefaultOAuth2AccessToken token = (DefaultOAuth2AccessToken) accessToken;
                    token.setAdditionalInformation(map);
                    return token;
                });
    }

}